#ifndef UFW_PROTOCOL_H
#define UFW_PROTOCOL_H

#include <cstdint>
#include <queue>
#include <functional>
#include "dev_ptl_model.h"

#define PTL_LAN_NUM 3               //IO口个数
#define TRANS_FILE_OVERTIME (10)    //传输文件10秒超时退出

#define JLPTL_HEAD 0xAA
#define JL_PTL_MSG_LEN 960

#define JL_PTL_JL_ACK_TRANS 1

enum {
    CFDL_FILE_REQ = 0x8003,
    CFDL_FILE_SLICE,
    CFDL_FILE_END,
};

enum {
    FILE_TRANS_REQ = 0,
    FILE_TRANS_SLICE,
    FILE_TRANS_END,
};

enum {
    FILE_TRANS_SUCCESS = 0,
    FILE_TRANS_FAILED,
    FILE_TRANS_OVERTIME,
};

//帧类型
typedef enum
{
    JL_FRAME_REQ_TYPE=0,//请求帧
    JL_FRAME_ACK_TYPE,//应答帧
    JL_FRAME_DAT_TYPE,//数据帧
    JL_FRAME_RESI_TYPE,//残帧
}JL_PTL_FRAME_TYPE;

//标准应答码
typedef enum
{
    JL_ACK_SUCCESS=0,           //标记命令执行成功
    JL_ACK_FAIL,                //标记命令执行失败
    JL_ACK_HARDWARE_ERROR,      //硬件忙或者硬件未就绪
    JL_ACK_NO_COMMAND,          //命令本模块不支持
    JL_ACK_ARG_ERROR,           //命令参数解析错误或参数越界
    JL_ACK_SYSTEM_ERROR,        //系统处于异常工作状态中
    JL_ACK_NO_SD,               //当前系统无SD卡
    JL_ACK_SD_FULL,             //当前系统SD卡满了
    JL_ACK_SD_INCOMPATIBLE,     //当前系统SD卡不兼容
    JL_ACK_SD_WRITE,            //当前系统SD卡写错误
    JL_ACK_SD_READ,             //当前系统SD卡读错误
    JL_ACK_SD_OP_FILE,          //当前系统SD卡操作失败
    JL_ACK_SD_FORMAT,           //当前系统SD卡格式化失败
    JL_ACK_SD_FILESYSTEM,       //当前系统SD卡文件系统错误
    JL_ACK_SD_FRAME,            //当前系统SD卡簇大小错误
    JL_ACK_FILE_CUT_BIG,        //文件切片太大
    JL_ACK_FILE_OPEN,           //无法打开文件
    JL_ACK_FILE_REOPEN,         //主机文件系统占线
    JL_ACK_FILE_DATA,           //文件未打开
    JL_ACK_FILE_READ,           //文件读取失败
    JL_ACK_TIME_OUT,            //操作超时
    JL_ACK_SN_RWRITE,           //SN重复写入
}JL_PTL_ACK;

#pragma pack (1)
typedef struct
{
    uint8_t start;    //0xAA
    uint8_t addr;     //源|目的地址
    uint8_t type;     //帧类型
    uint8_t seq;      //流水号
    uint16_t cmd;     //命令字
    uint8_t ack;      //应答码
    uint16_t payload_len; //payload长度
    uint8_t *payload;     //message
    uint16_t check;       //校验和
}jl_ptl_msg_t;

typedef struct __dfuinfo {
    uint32_t  DFU_head;           /*< DFU 头标记*/
    uint32_t  DFU_size;           /*< DFU 文件长度,注意此值大端对齐,小端机器使用请转换*/
    uint32_t  DFU_vid;            /*< DFU 版本标记,注意此值大端对齐,小端机器使用请转换*/
    uint32_t  DFU_pid;            /*< DFU 版本标记,注意此值大端对齐,小端机器使用请转换*/
    uint32_t  DFU_version;        /*< DFU 版本标记,注意此值大端对齐,小端机器使用请转换*/
    uint16_t  DFU_year;           /*< DFU 日期:年,注意2021内存表示为0x2021*/
    uint8_t   DFU_month;          /*< DFU 日期:月,注意12表示为0x12*/
    uint8_t   DFU_day;            /*< DFU 日期:日,注意12表示为0x12*/
    uint8_t   DFU_hour;           /*< DFU 日期:时,注意12表示为0x12*/
    uint8_t   DFU_minutes;        /*< DFU 日期:分,注意12表示为0x12*/
    uint8_t   DFU_seconds;        /*< DFU 日期:秒,注意12表示为0x12*/
    uint8_t   DFU_10ms;           /*< DFU 日期:10毫秒,注意12表示为0x12*/
    uint32_t  DFU_check;          /*< DFU 和校验/CRC32 注意此值大端对齐,小端机器使用请转换*/
}DFUinfo;
#pragma pack ()

typedef union
{
    jl_ptl_msg_t msg;
    uint8_t jl_ptl_data[sizeof(jl_ptl_msg_t)];
}jl_ptl_decode_msg_t;//用于解包的联合体

typedef struct
{
    uint8_t en;//使能标记
    uint8_t read_state;//读数据状态
    uint16_t check;//校验运算
    uint16_t payload_r;//当前接收的消息内容长度
    jl_ptl_decode_msg_t read_temp;//读数据缓存
}jl_ptl_io_node_t;

typedef struct _dfu_file_info {
    uint8_t file_state;
    uint8_t file_type;
    uint16_t slice_size;
    uint32_t file_size;
    uint32_t slice_total;

    uint32_t slice_index;
    uint32_t file_crc32;
}DFUFileTrans;

#define JLPTL_MSGLEN (sizeof(jl_ptl_msg_t))
#define JLPTL_MIN_MSGLEN (JLPTL_MSGLEN - 4)

#define JLPTL_RES_SND_ONE    0
#define JLPTL_RES_SND_REPEAT 1

class ufw_protocol : public DevPtlModel
{
public:
    explicit ufw_protocol();
    virtual ~ufw_protocol();

public:
    virtual void ptl_run(uint16_t ms);
    virtual int  ptl_io_in(uint8_t *data, uint16_t len);
    virtual void register_send_fun(std::function<void(const char*, uint16_t)>);

public:
    static const uint16_t crc16_ccitt_table[];
    static uint16_t crc_16(uint8_t *, uint16_t);
    static void crc_sum_16(uint16_t &crc, uint8_t *data, uint16_t len);

public:
    void jl_ptl_run(uint16_t ms);

    int jl_ptl_io_request();
    int jl_ptl_io_in(uint8_t port, uint8_t *data, uint16_t len);
    int jl_ptl_io_del(uint8_t port);

    void trans_file_data_push(const uint8_t*, uint32_t);
    uint32_t file_trans_index();
    bool is_file_trans_end();

    void file_trans_start(uint32_t file_size, uint16_t slice_size);
    int  file_trans_slice(uint32_t slice_num, uint16_t slice_size);
    void file_trans_end(uint8_t ret);

    int file_trans_slice_period();
    int file_trans_state();

private:
    uint32_t trans_file_data_pop(uint8_t*, uint32_t);

    void file_msg_parse(uint8_t port,jl_ptl_msg_t *msg);
    void trans_file_ack(jl_ptl_msg_t *msg);

    void file_data_send(uint16_t cmd, uint8_t ack, uint8_t *data, uint16_t data_len);
    void file_data_send_passthrough(uint16_t cmd, uint8_t ack, uint8_t *data, uint16_t data_len);

    int jl_ptl_res_send(uint8_t port, uint8_t type, jl_ptl_msg_t *msg);
    int jl_ptl_io_out(uint8_t port, jl_ptl_msg_t *msg);
    int jl_ptl_io_out_passthrough(uint8_t port, jl_ptl_msg_t *msg);

    void file_trans_offline();

private:
    jl_ptl_io_node_t _io_node_list[PTL_LAN_NUM];
    DFUFileTrans _file_trans;

    uint32_t _overtime   = 0;
    uint8_t  _re_snd_num = 0;
    uint8_t  _ptl_send_seq;

    int _trans_state = FILE_TRANS_REQ;

    std::queue<uint8_t> _file_data_fifo;
    std::function<void(const char*, uint16_t)> _send_data = nullptr;
};

#endif // UFW_PROTOCOL_H
